1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package storm.starter.tools;
19
20 import com.google.common.base.Throwables;
21 import com.google.common.collect.ImmutableList;
22 import com.google.common.collect.Lists;
23 import org.jmock.lib.concurrent.Blitzer;
24 import org.testng.annotations.DataProvider;
25 import org.testng.annotations.Test;
26
27 import java.util.List;
28
29 import static org.fest.assertions.api.Assertions.assertThat;
30
31 public class RankingsTest {
32
33 private static final int ANY_TOPN = 42;
34 private static final Rankable ANY_RANKABLE = new RankableObjectWithFields("someObject", ANY_TOPN);
35 private static final Rankable ZERO = new RankableObjectWithFields("ZERO_COUNT", 0);
36 private static final Rankable A = new RankableObjectWithFields("A", 1);
37 private static final Rankable B = new RankableObjectWithFields("B", 2);
38 private static final Rankable C = new RankableObjectWithFields("C", 3);
39 private static final Rankable D = new RankableObjectWithFields("D", 4);
40 private static final Rankable E = new RankableObjectWithFields("E", 5);
41 private static final Rankable F = new RankableObjectWithFields("F", 6);
42 private static final Rankable G = new RankableObjectWithFields("G", 7);
43 private static final Rankable H = new RankableObjectWithFields("H", 8);
44
45 @DataProvider
46 public Object[][] illegalTopNData() {
47 return new Object[][]{ { 0 }, { -1 }, { -2 }, { -10 } };
48 }
49
50 @Test(expectedExceptions = IllegalArgumentException.class, dataProvider = "illegalTopNData")
51 public void constructorWithNegativeOrZeroTopNShouldThrowIAE(int topN) {
52 new Rankings(topN);
53 }
54
55 @DataProvider
56 public Object[][] copyRankingsData() {
57 return new Object[][]{ { 5, Lists.newArrayList(A, B, C) }, { 2, Lists.newArrayList(A, B, C, D) },
58 { 1, Lists.newArrayList() }, { 1, Lists.newArrayList(A) }, { 1, Lists.newArrayList(A, B) } };
59 }
60
61 @Test(dataProvider = "copyRankingsData")
62 public void copyConstructorShouldReturnCopy(int topN, List<Rankable> rankables) {
63
64 Rankings rankings = new Rankings(topN);
65 for (Rankable r : rankables) {
66 rankings.updateWith(r);
67 }
68
69
70 Rankings copy = new Rankings(rankings);
71
72
73 assertThat(copy.maxSize()).isEqualTo(rankings.maxSize());
74 assertThat(copy.getRankings()).isEqualTo(rankings.getRankings());
75 }
76
77 @DataProvider
78 public Object[][] defensiveCopyRankingsData() {
79 return new Object[][]{ { 5, Lists.newArrayList(A, B, C), Lists.newArrayList(D) }, { 2, Lists.newArrayList(A, B, C,
80 D), Lists.newArrayList(E, F) }, { 1, Lists.newArrayList(), Lists.newArrayList(A) }, { 1, Lists.newArrayList(A),
81 Lists.newArrayList(B) }, { 1, Lists.newArrayList(ZERO), Lists.newArrayList(B) }, { 1, Lists.newArrayList(ZERO),
82 Lists.newArrayList() } };
83 }
84
85 @Test(dataProvider = "defensiveCopyRankingsData")
86 public void copyConstructorShouldReturnDefensiveCopy(int topN, List<Rankable> rankables, List<Rankable> changes) {
87
88 Rankings original = new Rankings(topN);
89 for (Rankable r : rankables) {
90 original.updateWith(r);
91 }
92 int expSize = original.size();
93 List<Rankable> expRankings = original.getRankings();
94
95
96 Rankings copy = new Rankings(original);
97 for (Rankable r : changes) {
98 copy.updateWith(r);
99 }
100
101
102 assertThat(original.size()).isEqualTo(expSize);
103 assertThat(original.getRankings()).isEqualTo(expRankings);
104 }
105
106 @DataProvider
107 public Object[][] legalTopNData() {
108 return new Object[][]{ { 1 }, { 2 }, { 1000 }, { 1000000 } };
109 }
110
111 @Test(dataProvider = "legalTopNData")
112 public void constructorWithPositiveTopNShouldBeOk(int topN) {
113
114 Rankings rankings = new Rankings(topN);
115
116
117 assertThat(rankings.maxSize()).isEqualTo(topN);
118 }
119
120 @Test
121 public void shouldHaveDefaultConstructor() {
122 new Rankings();
123 }
124
125 @Test
126 public void defaultConstructorShouldSetPositiveTopN() {
127
128 Rankings rankings = new Rankings();
129
130
131 assertThat(rankings.maxSize()).isGreaterThan(0);
132 }
133
134 @DataProvider
135 public Object[][] rankingsGrowData() {
136 return new Object[][]{ { 2, Lists.newArrayList(new RankableObjectWithFields("A", 1), new RankableObjectWithFields(
137 "B", 2), new RankableObjectWithFields("C", 3)) }, { 2, Lists.newArrayList(new RankableObjectWithFields("A", 1),
138 new RankableObjectWithFields("B", 2), new RankableObjectWithFields("C", 3), new RankableObjectWithFields("D",
139 4)) } };
140 }
141
142 @Test(dataProvider = "rankingsGrowData")
143 public void sizeOfRankingsShouldNotGrowBeyondTopN(int topN, List<Rankable> rankables) {
144
145 assertThat(rankables.size()).overridingErrorMessage(
146 "The supplied test data is not correct: the number of rankables <%d> should be greater than <%d>",
147 rankables.size(), topN).isGreaterThan(topN);
148
149
150 Rankings rankings = new Rankings(topN);
151
152
153 for (Rankable r : rankables) {
154 rankings.updateWith(r);
155 }
156
157
158 assertThat(rankings.size()).isLessThanOrEqualTo(rankings.maxSize());
159 }
160
161 @DataProvider
162 public Object[][] simulatedRankingsData() {
163 return new Object[][]{ { Lists.newArrayList(A), Lists.newArrayList(A) }, { Lists.newArrayList(B, D, A, C),
164 Lists.newArrayList(D, C, B, A) }, { Lists.newArrayList(B, F, A, C, D, E), Lists.newArrayList(F, E, D, C, B,
165 A) }, { Lists.newArrayList(G, B, F, A, C, D, E, H), Lists.newArrayList(H, G, F, E, D, C, B, A) } };
166 }
167
168 @Test(dataProvider = "simulatedRankingsData")
169 public void shouldCorrectlyRankWhenUpdatedWithRankables(List<Rankable> unsorted, List<Rankable> expSorted) {
170
171 Rankings rankings = new Rankings(unsorted.size());
172
173
174 for (Rankable r : unsorted) {
175 rankings.updateWith(r);
176 }
177
178
179 assertThat(rankings.getRankings()).isEqualTo(expSorted);
180 }
181
182 @Test(dataProvider = "simulatedRankingsData")
183 public void shouldCorrectlyRankWhenEmptyAndUpdatedWithOtherRankings(List<Rankable> unsorted,
184 List<Rankable> expSorted) {
185
186 Rankings rankings = new Rankings(unsorted.size());
187 Rankings otherRankings = new Rankings(rankings.maxSize());
188 for (Rankable r : unsorted) {
189 otherRankings.updateWith(r);
190 }
191
192
193 rankings.updateWith(otherRankings);
194
195
196 assertThat(rankings.getRankings()).isEqualTo(expSorted);
197 }
198
199 @Test(dataProvider = "simulatedRankingsData")
200 public void shouldCorrectlyRankWhenUpdatedWithEmptyOtherRankings(List<Rankable> unsorted, List<Rankable> expSorted) {
201
202 Rankings rankings = new Rankings(unsorted.size());
203 for (Rankable r : unsorted) {
204 rankings.updateWith(r);
205 }
206 Rankings emptyRankings = new Rankings(ANY_TOPN);
207
208
209 rankings.updateWith(emptyRankings);
210
211
212 assertThat(rankings.getRankings()).isEqualTo(expSorted);
213 }
214
215 @DataProvider
216 public Object[][] simulatedRankingsAndOtherRankingsData() {
217 return new Object[][]{ { Lists.newArrayList(A), Lists.newArrayList(A), Lists.newArrayList(A) },
218 { Lists.newArrayList(A, C), Lists.newArrayList(B, D), Lists.newArrayList(D, C, B, A) }, { Lists.newArrayList(B,
219 F, A), Lists.newArrayList(C, D, E), Lists.newArrayList(F, E, D, C, B, A) }, { Lists.newArrayList(G, B, F, A, C),
220 Lists.newArrayList(D, E, H), Lists.newArrayList(H, G, F, E, D, C, B, A) } };
221 }
222
223 @Test(dataProvider = "simulatedRankingsAndOtherRankingsData")
224 public void shouldCorrectlyRankWhenNotEmptyAndUpdatedWithOtherRankings(List<Rankable> unsorted,
225 List<Rankable> unsortedForOtherRankings, List<Rankable> expSorted) {
226
227 Rankings rankings = new Rankings(expSorted.size());
228 for (Rankable r : unsorted) {
229 rankings.updateWith(r);
230 }
231 Rankings otherRankings = new Rankings(unsortedForOtherRankings.size());
232 for (Rankable r : unsortedForOtherRankings) {
233 otherRankings.updateWith(r);
234 }
235
236
237 rankings.updateWith(otherRankings);
238
239
240 assertThat(rankings.getRankings()).isEqualTo(expSorted);
241 }
242
243 @DataProvider
244 public Object[][] duplicatesData() {
245 Rankable A1 = new RankableObjectWithFields("A", 1);
246 Rankable A2 = new RankableObjectWithFields("A", 2);
247 Rankable A3 = new RankableObjectWithFields("A", 3);
248 return new Object[][]{ { Lists.newArrayList(ANY_RANKABLE, ANY_RANKABLE, ANY_RANKABLE) }, { Lists.newArrayList(A1,
249 A2, A3) }, };
250 }
251
252 @Test(dataProvider = "duplicatesData")
253 public void shouldNotRankDuplicateObjectsMoreThanOnce(List<Rankable> duplicates) {
254
255 Rankings rankings = new Rankings(duplicates.size());
256
257
258 for (Rankable r : duplicates) {
259 rankings.updateWith(r);
260 }
261
262
263 assertThat(rankings.size()).isEqualTo(1);
264 }
265
266 @DataProvider
267 public Object[][] removeZeroRankingsData() {
268 return new Object[][]{ { Lists.newArrayList(A, ZERO), Lists.newArrayList(A) }, { Lists.newArrayList(A),
269 Lists.newArrayList(A) }, { Lists.newArrayList(ZERO, A), Lists.newArrayList(A) }, { Lists.newArrayList(ZERO),
270 Lists.newArrayList() }, { Lists.newArrayList(ZERO, new RankableObjectWithFields("ZERO2", 0)),
271 Lists.newArrayList() }, { Lists.newArrayList(B, ZERO, new RankableObjectWithFields("ZERO2", 0), D,
272 new RankableObjectWithFields("ZERO3", 0), new RankableObjectWithFields("ZERO4", 0), C), Lists.newArrayList(D, C,
273 B) }, { Lists.newArrayList(A, ZERO, B), Lists.newArrayList(B, A) } };
274 }
275
276 @Test(dataProvider = "removeZeroRankingsData")
277 public void shouldRemoveZeroCounts(List<Rankable> unsorted, List<Rankable> expSorted) {
278
279 Rankings rankings = new Rankings(unsorted.size());
280 for (Rankable r : unsorted) {
281 rankings.updateWith(r);
282 }
283
284
285 rankings.pruneZeroCounts();
286
287
288 assertThat(rankings.getRankings()).isEqualTo(expSorted);
289 }
290
291 @Test
292 public void updatingWithNewRankablesShouldBeThreadSafe() throws InterruptedException {
293
294 final List<Rankable> entries = ImmutableList.of(A, B, C, D);
295 final Rankings rankings = new Rankings(entries.size());
296
297
298
299
300 final List<Exception> exceptions = Lists.newArrayList();
301 Blitzer blitzer = new Blitzer(1000);
302
303
304 blitzer.blitz(new Runnable() {
305 public void run() {
306 for (Rankable r : entries) {
307 try {
308 rankings.updateWith(r);
309 }
310 catch (RuntimeException e) {
311 synchronized(exceptions) {
312 exceptions.add(e);
313 }
314 }
315 }
316 }
317 });
318 blitzer.shutdown();
319
320
321
322 if (!exceptions.isEmpty()) {
323 for (Exception e : exceptions) {
324 System.err.println(Throwables.getStackTraceAsString(e));
325 }
326 }
327 assertThat(exceptions).isEmpty();
328 }
329
330 @Test(dataProvider = "copyRankingsData")
331 public void copyShouldReturnCopy(int topN, List<Rankable> rankables) {
332
333 Rankings rankings = new Rankings(topN);
334 for (Rankable r : rankables) {
335 rankings.updateWith(r);
336 }
337
338
339 Rankings copy = rankings.copy();
340
341
342 assertThat(copy.maxSize()).isEqualTo(rankings.maxSize());
343 assertThat(copy.getRankings()).isEqualTo(rankings.getRankings());
344 }
345
346 @Test(dataProvider = "defensiveCopyRankingsData")
347 public void copyShouldReturnDefensiveCopy(int topN, List<Rankable> rankables, List<Rankable> changes) {
348
349 Rankings original = new Rankings(topN);
350 for (Rankable r : rankables) {
351 original.updateWith(r);
352 }
353 int expSize = original.size();
354 List<Rankable> expRankings = original.getRankings();
355
356
357 Rankings copy = original.copy();
358 for (Rankable r : changes) {
359 copy.updateWith(r);
360 }
361 copy.pruneZeroCounts();
362
363
364 assertThat(original.size()).isEqualTo(expSize);
365 assertThat(original.getRankings()).isEqualTo(expRankings);
366 }
367
368 }